#include "StdAfx.h"
#include "StrokeFiller.h"
#include "StrokeData.h"
#include "FillStrokeTool.h"
#include <memory.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

///////////////////////////////////////////////////////////////////////////////

void DbgPrintf(const char *szFmt, ...);

#ifndef PROFILE
inline void DbgPrintf(const char *szFmt, ...) {}
#endif	// PROFILE

///////////////////////////////////////////////////////////////////////////////

struct SFillerEnv
{
public:
	SFillerEnv();
	~SFillerEnv();

public:
	static SFillerEnv* FromHandle(HANDLE h);
	int freeInput();
	int freeGuide();
	int destruct();
	BOOL setContour(const SPoint *lpPoint, int nPoint);
	BOOL setMidline(const SPoint *lpPoint, int nPoint);
	BOOL setGuideLine(const SPoint *lpPoint, int nPoint);
    BOOL setStartXY(short x, short y);
	int getFillPoint(SPoint *lpPoint, int nPoint);

protected:
	DWORD	m_dwTag;

public:
	int		m_nMaxX;
	int		m_nMaxY;
	int		m_nReadIdx;

protected:
	SPoint	*m_pContour;
	int		m_nContour;
	SPoint	*m_pMidline;
	int		m_nMidline;
    SPoint  mStartPoint;

protected:
	SPoint	*m_pGuide;
	int		m_nGuide;
	SPoint	*m_pFill;
	int		m_nFill;

protected:
	BOOL createGuide(void);
	BOOL createFillList(void);

protected:
	SFillerEnv(const SFillerEnv&);
	SFillerEnv& operator=(const SFillerEnv&);
};

const char FILLER_TAG[] = "fil\x1";
SFillerEnv::SFillerEnv()
{
	memset(this, 0, sizeof(SFillerEnv));
	m_dwTag = *(DWORD *)FILLER_TAG;
	m_nMaxX = 267;
	m_nMaxY = 267;
    mStartPoint.x = (short)(-1);
    mStartPoint.y = (short)(-1);
}

SFillerEnv::~SFillerEnv()
{
	m_dwTag = 0;
	freeInput();
	destruct();
}

SFillerEnv* SFillerEnv::FromHandle(HANDLE h)
{
	if (!h)
		return NULL;
	SFillerEnv *p = (SFillerEnv *)h;
	if (p->m_dwTag != *(DWORD *)FILLER_TAG)
		return NULL;
	return p;
}

int SFillerEnv::freeInput()
{
	if (m_pContour != NULL) {
		delete[] m_pContour;
		m_pContour = NULL;
		m_nContour = 0;
	}
	if (m_pMidline != NULL) {
		delete[] m_pMidline;
		m_pMidline = NULL;
		m_nMidline = 0;
	}
	return 0;
}

int SFillerEnv::freeGuide()
{
	if (m_pGuide != NULL) {
		delete[] m_pGuide;
		m_pGuide = NULL;
		m_nGuide = 0;
	}
	return 0;
}

int SFillerEnv::destruct()
{
	freeGuide();
	if (m_pFill != NULL) {
		delete[] m_pFill;
		m_pFill = NULL;
		m_nFill = 0;
	}
	return 0;
}

BOOL SFillerEnv::setContour(const SPoint *lpPoint, int nPoint)
{
	if (!lpPoint || nPoint<=2)
		return FALSE;
	if (m_pContour != NULL) {
		delete[] m_pContour;
		m_pContour = NULL;
		m_nContour = 0;
	}
	if ((m_pContour = new SPoint[nPoint]) == NULL)
		return FALSE;
	memcpy(m_pContour, lpPoint, sizeof(SPoint)*nPoint);
	m_nContour = nPoint;
	m_nReadIdx = 0;
	return TRUE;
}

BOOL SFillerEnv::setMidline(const SPoint *lpPoint, int nPoint)
{
	if (!lpPoint || nPoint<=1)
		return FALSE;
	if (m_pMidline != NULL) {
		delete[] m_pMidline;
		m_pMidline = NULL;
		m_nMidline = 0;
	}
	if ((m_pMidline = new SPoint[nPoint]) == NULL)
		return FALSE;
	memcpy(m_pMidline, lpPoint, sizeof(SPoint)*nPoint);
	m_nMidline = nPoint;
	m_nReadIdx = 0;
	return TRUE;
}

BOOL SFillerEnv::setGuideLine(const SPoint *lpPoint, int nPoint)
{
	if (!lpPoint || nPoint<=1)
		return FALSE;
	freeGuide();
	if ((m_pGuide = new SPoint[nPoint]) == NULL)
		return FALSE;
	memcpy(m_pGuide, lpPoint, sizeof(SPoint)*nPoint);
	m_nGuide = nPoint;
	m_nReadIdx = 0;
	return TRUE;
}

BOOL SFillerEnv::setStartXY(short x, short y)
{
    if (x<0 || x>m_nMaxX || y<0 || y>m_nMaxY)
        return FALSE;
    mStartPoint.x = x;
    mStartPoint.y = y;
    return TRUE;
}

int SFillerEnv::getFillPoint(SPoint *lpPoint, int nPoint)
{
	if (!m_pFill) {
        if (!m_pGuide && m_pMidline!=NULL && !createGuide()) {
            DbgPrintf("fail in createGuide.");
            return 0;
        }
		if (!m_pFill && !createFillList()) {
			DbgPrintf("fail in createFillList.");
			return 0;
		}
	}
	if (!lpPoint)
		return m_nFill;
	if (m_nReadIdx >= m_nFill)
		return 0;
	int nMax = min(nPoint, m_nFill-m_nReadIdx);
	memcpy(lpPoint, m_pFill+m_nReadIdx, sizeof(SPoint)*nMax);
	m_nReadIdx += nMax;
	return nMax;
}

///////////////////////////////////////////////////////////////////////////////

BOOL SFillerEnv::createGuide(void)
{
	if (!m_pMidline || !m_pContour || m_pGuide!=NULL)
		return FALSE;
	CStrokeData sd;
	sd.m_lpSrcPoint = m_pMidline;
	sd.m_nSrcPoint = m_nMidline;
	if (!sd.createReducePoint(m_nMaxX, m_nMaxY, NULL, FALSE, NULL)) {
		sd.m_lpSrcPoint = NULL;
		return FALSE;
	}
	sd.m_lpSrcPoint = NULL;
	// to create output BYTEs
	int nOut = sd.m_nPoint;
	ASSERT(0 < nOut && nOut < 256);
	if ((m_pGuide = new SPoint[nOut]) == NULL)
		return FALSE;
	for(int i=0; i<nOut; i++)
		m_pGuide[i] = sd.m_ptBuf[i];
	m_nGuide = nOut;
	return TRUE;
}

BOOL SFillerEnv::createFillList(void)
{
	if (m_pFill != NULL)
		return FALSE;
	CCheckInContour check;
	CFillStrokeTool tool(&check);
	check.setMaxSize(m_nMaxX, m_nMaxY);
	check.setContour(m_pContour, m_nContour);
	tool.setGuides(m_pGuide, m_nGuide);
    tool.setStartXY(mStartPoint.x, mStartPoint.y);
	m_nFill = tool.getPoints(&m_pFill);
	if (m_nFill <= 0)
		return FALSE;
	freeInput();
	freeGuide();
	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////

STROKE_FILLER_API HANDLE OUTAPI createFiller(void)
{
	SFillerEnv *p = new SFillerEnv();
	return (HANDLE)p;
}

STROKE_FILLER_API BOOL OUTAPI setMaxSize(HANDLE hFiller, int nMaxX, int nMaxY)
{
	SFillerEnv *p = SFillerEnv::FromHandle(hFiller);
	if (!p)
		return FALSE;
	if (nMaxX<=0 || nMaxY<=0)
		return FALSE;
	p->m_nMaxX = nMaxX;
	p->m_nMaxY = nMaxY;
	return TRUE;
}

STROKE_FILLER_API BOOL OUTAPI setData(HANDLE hFiller, const short *pnContour, int nCount1, const short *pnMidline, int nCount2)
{
	SFillerEnv *p = SFillerEnv::FromHandle(hFiller);
	if (!p)
		return FALSE;
	if (!pnContour || (nCount1 & 1) || nCount2<0 || (nCount2 & 1))
		return FALSE;
	p->freeInput();
	p->destruct();
	if (!p->setContour((const SPoint *)pnContour, nCount1/2))
		return FALSE;
	// pnMidline may be NULL
	if (pnMidline!=NULL && !p->setMidline((const SPoint *)pnMidline, nCount2/2))
		return FALSE;
	return TRUE;
}

STROKE_FILLER_API BOOL OUTAPI setGuide(HANDLE hFiller, const short *pnGuide, int nGuide)
{
	SFillerEnv *p = SFillerEnv::FromHandle(hFiller);
	if (!p)
		return FALSE;
	if (!pnGuide || (nGuide & 1))
		return FALSE;
	return p->setGuideLine((const SPoint *)pnGuide, nGuide/2);
}

STROKE_FILLER_API BOOL OUTAPI setStartXY(HANDLE hFiller, short x, short y)
{
    SFillerEnv *p = SFillerEnv::FromHandle(hFiller);
    if (!p)
        return FALSE;
    return p->setStartXY(x, y);
}

STROKE_FILLER_API int OUTAPI getPoints(HANDLE hFiller, short *pnBuf, int nBuf)
{
	SFillerEnv *p = SFillerEnv::FromHandle(hFiller);
	if (!p)
		return -1;
	return p->getFillPoint((SPoint *)pnBuf, nBuf/2) * 2;
}

STROKE_FILLER_API BOOL OUTAPI destroyFiller(HANDLE hFiller)
{
	SFillerEnv *p = SFillerEnv::FromHandle(hFiller);
	if (!p)
		return FALSE;
	delete p;
	return TRUE;
}
